#include "client.hh"

#include <request/request-decorator.hh>

#include "octree/functor.hh"
#include "octree/octree_rts.hh"

#include "map/perlin.hh"
#include "request/request-server.hh"
#include "request/request-creator-server.hh"
#include "network-error-handler-server.hh"
#include "game-thread.hh"
#include "fps-thread.hh"

Client::Client (tcp::socket* s, Database* db,
		GameThread* gt):
  id_ (-1),
  pos_x_ (0),
  pos_y_ (0),
  nm_ (s, new RequestCreatorServer(&id_, this), new NetworkErrorHandlerServer ()),
  db_ (db),
  gt_ (gt),
  fps_thread_ (0),
  locked_ (false)
{
   t_ = boost::thread (&Client::start, this);
}

Client::~Client ()
{
  std::cout << "deleting client" << std::endl;
  //  delete rcs_;
}

void
Client::start ()
{
  std::cout << "New connection" << std::endl;

  boost::shared_ptr<Request> rq = nm_.read ();
  boost::shared_ptr<ConnectionServer> cs = boost::dynamic_pointer_cast<ConnectionServer> (rq);

  if (!cs.use_count ())
  {
    std::cout << "Wrong connection request" << std::endl;
    t_.detach ();
    return;
  }

  if (cs->identification (db_))
  {
    nm_.send (RType::CONNECTION, cs->flag ());
    send_map (OctreeRts::Instance ());
  }
  else
  {
    nm_.send (RType::CONNECTION, cs->flag ());
    return;
  }

  while (!nm_.eh ()->error () && !nm_.rc ()->disconnecting ())
  {
    boost::shared_ptr<Request> r = nm_.read ();
    if (!nm_.eh ()->error ())
    {
      if (fps_thread_)
	fps_thread_->push (r);
      else
	gt_->push (r);
    }
  }

  if (!nm_.rc ()->disconnecting ())
    gt_->push ((*(nm_.rc ())) (RType::DISCONNECTION, ""));

  if (fps_thread_)
    fps_thread_->remove_player (id_);

  t_.detach ();
  //  nm_.s ()->shutdown (tcp::socket::shutdown_both);
  //  nm_.s ()->close ();
}

void
Client::send_map (Octree* o)
{
  Map* map = new Map (o->size (), o->size ());

  nm_.send (RType::ASK_MAP, map->tonet ());

  // if (!oct->map_is_loaded_get ())
  // {
  //   PerlinNoise* p = new PerlinNoise (oct->size_get ());
  //   Map* map = p->perform (0.6, 4);
  //   // Map* map = new Map (512, 512);
  //   oct->insert_map (map);
  //   delete map;
  // }
  MapFunctor mf (this);

  o->accept (&mf);
  nm_.flush ();
}

void
Client::fps_thread (FpsThread* t)
{
  std::cout << "Client " << id_ << " is Entering FPS Mode " << std::endl;
  fps_thread_ = t;
}

GameMode::GameType
Client::mode () const
{
  if (fps_thread_)
    return GameMode::Fps;

  return GameMode::Rts;
}
